/*
 * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief Public APIs.
 */

#ifndef _FLASHDB_H_
#define _FLASHDB_H_

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
#include <fdb_cfg.h>

#ifdef FDB_USING_FAL_MODE

#include <../port/fal/inc/fal.h>

#endif
#define db_name(db)                              (((fdb_db_t)db)->name)
#define db_init_ok(db)                           (((fdb_db_t)db)->init_ok)
#define db_sec_size(db)                          (((fdb_db_t)db)->sec_size)
#define db_max_size(db)                          (((fdb_db_t)db)->max_size)

#include <fdb_def.h>
#include "fdb_low_lvl.h"

#ifndef FDB_WRITE_GRAN
#error "Please configure flash write granularity (in fdb_cfg.h)"
#endif

#if FDB_WRITE_GRAN != 1 && FDB_WRITE_GRAN != 8 && FDB_WRITE_GRAN != 32
#error "the write gran can be only setting as 1, 8 and 32"
#endif
#define db_lock(db)                             \
do                                          \
{                                           \
if (((fdb_db_t)db)->lock)               \
((fdb_db_t)db)->lock((fdb_db_t)db); \
} while (0);

#define db_unlock(db)                             \
do                                            \
{                                             \
if (((fdb_db_t)db)->unlock)               \
((fdb_db_t)db)->unlock((fdb_db_t)db); \
} while (0);

/* magic word(`F`, `D`, `B`, `1`) */
#define SECTOR_MAGIC_WORD 0x34424446
#ifndef KV_MAGIC_WORD
/* magic word(`K`, `V`, `0`, `0`) */
#define KV_MAGIC_WORD 0x3530564B
#endif

/* the sector remain threshold before full status */
#ifndef FDB_SEC_REMAIN_THRESHOLD
#define FDB_SEC_REMAIN_THRESHOLD (KV_HDR_DATA_SIZE + FDB_KV_NAME_MAX)
#endif

/* the total remain empty sector threshold before GC */
#ifndef FDB_GC_EMPTY_SEC_THRESHOLD
#define FDB_GC_EMPTY_SEC_THRESHOLD 32
#endif

/* the string KV value buffer size for legacy fdb_get_kv(db, ) function */
#ifndef FDB_STR_KV_VALUE_MAX_SIZE
#define FDB_STR_KV_VALUE_MAX_SIZE 128
#endif

#if FDB_KV_CACHE_TABLE_SIZE > 0xFFFF
#error "The KV cache table size must less than 0xFFFF"
#endif

/* the sector is not combined value */
#define SECTOR_NOT_COMBINED 0xFFFFFFFF
/* the next address is get failed */
#define FAILED_ADDR 0xFFFFFFFF

#define KV_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_KV_STATUS_NUM)
struct kv_hdr_data {
    uint8_t status_table[KV_STATUS_TABLE_SIZE]; /**< KV node status, @see fdb_kv_status_t */
    uint32_t magic;                             /**< magic word(`K`, `V`, `4`, `0`) */
    uint32_t len;                               /**< KV node total length (header + name + value), must align by FDB_WRITE_GRAN */
    uint32_t crc32;                             /**< KV node crc32(name_len + data_len + name + value) */
    uint8_t name_len;                           /**< name length */
    uint32_t value_len;                         /**< value length */
    uint32_t type;
};
typedef struct kv_hdr_data *kv_hdr_data_t;
#define db_name(db) (((fdb_db_t)db)->name)
#define db_init_ok(db) (((fdb_db_t)db)->init_ok)
#define db_sec_size(db) (((fdb_db_t)db)->sec_size)
#define db_max_size(db) (((fdb_db_t)db)->max_size)
#define SECTOR_HDR_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct sector_hdr_data_kv)))
#define SECTOR_DIRTY_OFFSET ((unsigned long)(&((struct sector_hdr_data_kv *)0)->status_table.dirty))
#define KV_HDR_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct kv_hdr_data)))
#define KV_MAGIC_OFFSET ((unsigned long)(&((struct kv_hdr_data *)0)->magic))
#define KV_LEN_OFFSET ((unsigned long)(&((struct kv_hdr_data *)0)->len))
#define KV_NAME_LEN_OFFSET ((unsigned long)(&((struct kv_hdr_data *)0)->name_len))

/* FlashDB database API */
fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *path, struct fdb_default_kv *default_kv,
                        void *user_data);

void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg);

fdb_err_t fdb_kvdb_deinit(fdb_kvdb_t db);

fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *path, fdb_get_time get_time, size_t max_len,
                        void *user_data);

void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg);

fdb_err_t fdb_tsdb_deinit(fdb_tsdb_t db);

/* blob API */
fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len);

size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob);

/* Key-Value API like a KV DB */
fdb_err_t fdb_kv_set(fdb_kvdb_t db, const char *key, const char *value);

char *fdb_kv_get(fdb_kvdb_t db, const char *key);

fdb_err_t fdb_kv_set_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob, uint8_t type);

size_t fdb_kv_get_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob);

fdb_err_t fdb_kv_del(fdb_kvdb_t db, const char *key);

fdb_kv_t fdb_kv_get_obj(fdb_kvdb_t db, const char *key, fdb_kv_t kv);

fdb_blob_t fdb_kv_to_blob(fdb_kv_t kv, fdb_blob_t blob);

fdb_err_t fdb_kv_set_default(fdb_kvdb_t db);

void fdb_kv_print(fdb_kvdb_t db);

bool print_kv_cb(fdb_kv_t kv, void *arg1, void *arg2);

fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kv_iterator_t itr);

bool fdb_kv_iterate(fdb_kvdb_t db, fdb_kv_iterator_t itr);

/* Time series log API like a TSDB */
fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob);

void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg);

void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg);

size_t fdb_tsl_query_count(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status);

fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status);

void fdb_tsl_clean(fdb_tsdb_t db);

fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob);

/* fdb_utils.c */
uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size);

void kv_iterator(fdb_kvdb_t db, fdb_kv_t kv, void *arg1, void *arg2,
                 bool (*callback)(fdb_kv_t kv, void *arg1, void *arg2));

fdb_err_t write_kv_hdr(fdb_kvdb_t db, uint32_t addr, kv_hdr_data_t kv_hdr);

bool find_kv(fdb_kvdb_t db, const char *key, fdb_kv_t kv);

uint32_t new_kv_ex(fdb_kvdb_t db, kv_sec_info_t sector, size_t key_len, size_t buf_len);

fdb_err_t del_kv(fdb_kvdb_t db, const char *key, fdb_kv_t old_kv, bool complete_del);

fdb_err_t
create_kv_blob(fdb_kvdb_t db, kv_sec_info_t sector, const char *key, const void *value, size_t len, uint8_t type);

uint32_t new_kv(fdb_kvdb_t db, kv_sec_info_t sector, size_t kv_size);

fdb_err_t update_sec_status(fdb_kvdb_t db, kv_sec_info_t sector, size_t new_kv_len, bool *is_full);

fdb_err_t align_write(fdb_kvdb_t db, uint32_t addr, const uint32_t *buf, size_t size);

void update_sector_cache(fdb_kvdb_t db, uint32_t sec_addr, uint32_t empty_addr);

void update_kv_cache(fdb_kvdb_t db, const char *name, size_t name_len, uint32_t addr);
fdb_err_t kv_read_sector_info(fdb_kvdb_t db, uint32_t addr, kv_sec_info_t sector, bool traversal);
fdb_err_t read_kv(fdb_kvdb_t db, fdb_kv_t kv);
uint32_t get_next_kv_addr(fdb_kvdb_t db, kv_sec_info_t sector, fdb_kv_t pre_kv);
uint32_t kv_get_next_sector_addr(fdb_kvdb_t db, kv_sec_info_t pre_sec);
bool fdb_is_str(uint8_t *value, size_t len);

#endif /* _FLASHDB_H_ */
